import { CacheService } from '@app/cache';
import { ConfigureService } from '@app/configure';
import { BaseService } from '@app/db/base/base.service';
import { ChargeOrder } from '@app/db/entities/ChargeOrder';
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DataSource, FindOptionsWhere, In, LessThan, Repository } from 'typeorm';
import { DeviceService } from '../device/device.service';
import { Device } from '@app/db/entities/Device';
import { CommonService } from '../common/common.service';
import { OrderPowerData, OrderPowerDataDocument } from '@app/mongodb/schemas/orderPowerData';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Merchant } from '@app/db/entities/Merchant';
import { WechatService } from '../wechat/wechat.service';

@Injectable()
export class ChargeOrderService extends BaseService<ChargeOrder> {
    constructor(
        @InjectRepository(ChargeOrder) protected repository:Repository<ChargeOrder>,
        @InjectRepository(Merchant) protected merchant:Repository<Merchant>,


        @InjectModel(OrderPowerData.name) public readonly orderPowerData: Model<OrderPowerDataDocument>,
        protected dataSource: DataSource,
        private readonly common:CommonService,
        private readonly device:DeviceService,
        private readonly config:ConfigureService,
        private readonly cache:CacheService,
        private readonly wechat:WechatService,


    ){
        super();
        this.primaryKey = 'id'
    }

    public queryMerchant(id:number, fields:(keyof Merchant)[]) {
        return this.merchant.findOne({
            where : {id : id},
            select : fields
        })
    }

    public generateOrderNo(userId:number) {
        const date = new Date()
        const time = Math.floor(date.getTime() / 1000) % 1000000
        const year = date.getFullYear() % 100
        const month = String(date.getMonth() + 1).padStart(2, '0')
        const day = String(date.getDate()).padStart(2, '0')
        return year + month + day + time + String(userId).padStart(10, '0')
    }

    async charging(userId:number, init:number) {
        const key = this.config.get('userChargingOrderKey') + userId
        const orderIds = await this.cache.client.smembers(key)
        if(!orderIds || orderIds.length == 0) return []
        const orders = await this.find({id : orderIds.length == 1 ? Number(orderIds[0]) : In(orderIds)}, ['id', 'orderNo', 'status', 'deviceId', 'port', 'feeType', 'precharge'])
        

        const outOrderIds:number[] = []
        const deviceIds:number[] = []
        orders.forEach(order => {
            if(order.status == 1) {
                deviceIds.push(order.deviceId)
            }else {
                outOrderIds.push(order.id)
            }
        })

        if(outOrderIds.length > 0) {
            this.cache.client.srem(key, ...outOrderIds)
        }

        if(deviceIds.length == 0) return []

        let devices = await this.device.find({id : deviceIds.length == 1 ? deviceIds[0] : In(deviceIds)}, ['id', 'chargeType', 'portData', 'aliProduct'])
        const deviceMap:{[key:string] : Device} = {}
        devices.forEach(device => {
            if(init == 1) {
                this.device.queryDeviceChargingStatus(device.id, device.aliProduct)
            }
            
            deviceMap[device.id] = device
        })
        devices = null
        
        const rows:any = []
        
        orders.forEach(order => {
            if(order.status == 1) {
                let row:any = {...order}
                const device = deviceMap[order.deviceId]
                if(device) {
                    row.device = {
                        chargeType : device.chargeType
                    }
                    if(device.portData) {
                        row.device.port = device.portData[order.port - 1]
                    }
                }
                rows.push(row)
            }
        })

        return rows

    }

    async detail(id:number, userId) {
        const order = await this.findOne({id}, ['userId', 'orderNo', 'id', 'deviceId', 'errorCode', 'feeType', 'precharge', 'chargeData', 'port', 'consumeKwh', 'consumeAmount', 'stopReason', 'createTime', 'endTime', 'status'])
        if(!order || order.userId != userId) return null
        const device = await this.device.findOne({id : order.deviceId}, ['chargeType'])

        let failReason = ''

        if(order.status == 3) {
            failReason = await this.common.queryChargeFailReason(order.errorCode)
        }

        const stopReason = await this.common.queryChargeStopReason(order.stopReason)
        return {
            ...order,
            stopReason,
            failReason,
            device
        }
    }

    async page(query:Pagination, userId:number) {
        let where:FindOptionsWhere<ChargeOrder> = { userId }
        const lastId = query.lastKey || 0
        const pageSize = query.pageSize || 20

        if(lastId > 0) {
            where.id = LessThan(lastId)
        }

        let rows:(ChargeOrder & {failReason?:string})[] = await this.repository.find({
            where,
            select : ['id', 'orderNo', 'deviceId', 'createTime', 'endTime', 'status', 'consumeAmount', 'port', 'errorCode'], 
            skip : 0, 
            take : pageSize, 
            order : {id : 'DESC'}
        })

        const errorCodes:number[] = []
        rows.forEach(row => {
            if(row.status == 3 && !errorCodes.includes(row.errorCode)) {
                errorCodes.push(row.errorCode)
            }
        })

        if(errorCodes.length > 0) {
            const reasons = await this.common.queryChargeFailReasons()
            rows.forEach(row => {
                if(row.status == 3 && reasons[row.errorCode] != undefined) {
                    row.failReason = reasons[row.errorCode]
                }
            })
        }

        return rows
    }

    params(id:number, pageNo:number, pageSize:number) {
        return this.orderPowerData.findOne({orderId : id}, {_id : 0, data : {$slice : ['$data', (pageNo - 1) * pageSize, pageSize]}})
    }

    async payscoreNotify(id:number, data, isWx:boolean) {
        const lockKey= 'pc:p:' + id
        const lock = await this.cache.set(lockKey, '1', 20, 'NX')
        if(!lock) return false
        if(isWx) {
            try {
                const res = await this.wechat.decipher_gcm(data.ciphertext, data.associated_data, data.nonce)
                // if(res.) {}
                // this.repository.update({id : userId}, {payscore : status})
                this.repository.update({id}, {payStatus : 1})
                this.cache.del(lockKey)

                return true

            } catch (error) {
                
                console.log(error)
                
            }

            return false
           
        }else { //支付宝

        }
    }
}
